#include "Rasterizer.h"

namespace MASGK
{

	CRasterizer::CRasterizer(void)
	{
		m_Buffer = new CTgaBuffer(256, 256);
		m_Buffer->Clear(float3(0.0f, 0.0f, 0.0f));
		m_RcWidth = m_Buffer->GetWidth() * 1.0f;
		m_RcHeight = m_Buffer->GetHeight() * 1.0f;
	}


	CRasterizer::~CRasterizer(void)
	{
		delete m_Buffer;
	}	

	void CRasterizer::Triangle(float3& _a, float3& _b, float3& _c, color& _colA, color& _colB, color& _colC)
	{
		float y1Canon = _a.y;
		float y2Canon = _b.y;
		float y3Canon = _c.y;

		float x1Canon = _a.x;
		float x2Canon = _b.x;
		float x3Canon = _c.x;

		// For light
		float3 normal = (m_Vp->CountNormal(_a, _b, _c)).normalize();
		float zPos = (_a.z + _b.z + _c.z) / 3.0f;

		int rWidth = m_Buffer->GetWidth();
		int rHeight = m_Buffer->GetHeight();

		// Bounding rectangle
		int minx = (int) min(Canonical2RcCoord(x1Canon, rWidth), Canonical2RcCoord(x2Canon, rWidth), Canonical2RcCoord(x3Canon, rWidth));
		int maxx = (int) max(Canonical2RcCoord(x1Canon, rWidth), Canonical2RcCoord(x2Canon, rWidth), Canonical2RcCoord(x3Canon, rWidth));
		int miny = (int) min(Canonical2RcCoord(y1Canon, rHeight), Canonical2RcCoord(y2Canon, rHeight), Canonical2RcCoord(y3Canon, rHeight));
		int maxy = (int) max(Canonical2RcCoord(y1Canon, rHeight), Canonical2RcCoord(y2Canon, rHeight), Canonical2RcCoord(y3Canon, rHeight));

		minx = max(minx, 0, 0);
		maxx = min(maxx, rWidth - 1, rWidth - 1);
		miny = max(miny, 0, 0);
		maxy = min(maxy, rHeight - 1, rHeight - 1);

		int x1 = Canonical2RcCoord(x1Canon, rWidth);
		int x2 = Canonical2RcCoord(x2Canon, rWidth);
		int x3 = Canonical2RcCoord(x3Canon, rWidth);

		int y1 = Canonical2RcCoord(y1Canon, rHeight);
		int y2 = Canonical2RcCoord(y2Canon, rHeight);
		int y3 = Canonical2RcCoord(y3Canon, rHeight);

		// Optimalization
		int dx12 = x1 - x2;
		int dx23 = x2 - x3;
		int dx31 = x3 - x1;
		int dy12 = y1 - y2;
		int dy23 = y2 - y3;
		int dy31 = y3 - y1;

		// Baricentric coords
		int dx13 = x1 - x3;
		int dx32 = x3 - x2;
		int dy13 = y1 - y3;
		float lambda1 = 0.0f;
		float lambda2 = 0.0f;
		float lambda3 = 0.0f;

		uint color;

		// Top-left algorithm
		bool tl1 = false;
		bool tl2 = false;
		bool tl3 = false;

		if (dy12 < 0 || (dy12 == 0 && dx12 > 0))
			tl1 = true;

		if (dy23 < 0 || (dy23 == 0 && dx23 > 0))
			tl2 = true;

		if (dy31 < 0 || (dy31 == 0 && dx31 > 0))
			tl3 = true;

		// Z-buffer
		float depth;

		// Scan through bounding rectangle
		for(int y = miny; y < maxy; y++)
		{
			for(int x = minx; x < maxx; x++)
			{
				// When all half-space functions positive, pixel is in triangle
				if(		(tl1 && (dx12 * (y - y1) - dy12 * (x - x1) >= 0) || (dx12 * (y - y1) - dy12 * (x - x1)) > 0) &&
						(tl2 && (dx23 * (y - y2) - dy23 * (x - x2) >= 0) || (dx23 * (y - y2) - dy23 * (x - x2) > 0)) &&
						(tl3 && (dx31 * (y - y3) - dy31 * (x - x3) >= 0) || (dx31 * (y - y3) - dy31 * (x - x3) > 0)))
				{
					lambda1 = (dy23 * (x - x3) + dx32 * (y - y3)) * 1.0f / (dy23 * dx13 + dx32 * dy13) * 1.0f;
					lambda2 = (dy31 * (x - x3) + dx13 * (y - y3)) * 1.0f / (dy31 * dx23 + dx13 * dy23) * 1.0f;
					lambda3 = 1.0f - lambda1 - lambda2;

					if (lambda3 > 0.330f && lambda3 < 0.339 && lambda1 > 0.330f && lambda1 < 0.339 && lambda2 > 0.330f && lambda2 < 0.339)
						int br = 0;

					depth = lambda1 * _a.z + lambda2 * _b.z + lambda3 * _c.z;

					if (depth > m_Buffer->GetDepth(x, y))
					{
						color = m_Buffer->To32BitColor(lambda1 * _colA + lambda2 * _colB + lambda3 * _colC);
						
						// Light
						color = PixelLight(x, y, zPos, rWidth, rHeight, normal, color);

						m_Buffer->SetPixel(x, y, color);
						m_Buffer->SetDepth(x, y, depth);
					}
				}
			}
		}		
	}

	void CRasterizer::Triangle(float3& _a, float3& _b, float3& _c, color& _color)
	{
		return Triangle(_a, _b, _c, _color, _color, _color);
	}

	void CRasterizer::Save()
	{
		m_Buffer->Save(m_Buffer->GetWidth(), m_Buffer->GetHeight(), "MASGK.tga");
	}

	int CRasterizer::Canonical2RcCoord(float & _coord, int _size)
	{
		return (int) ((_coord + 1.0f) * _size * 0.5f);
	}

	float CRasterizer::Rc2CannonicalCoord(int & _coord, int _size)
	{
		return ((_coord * 1.0f) / (_size * 0.5f)) - 1.0f;
	}

	uint CRasterizer::PixelLight(int _x, int _y, float _zPos, int _width, int _height, float3 _normal, uint _color)
	{
		uint retColor = 0xFF000000;
		int r = (_color >> 16) & 0xFF;
		int g = (_color >> 8) & 0xFF;
		int b = _color & 0xFF;

		// ... Count L (check if light influance)
		float3 pos = 				
					float3
						(
							Rc2CannonicalCoord(_x, _width),
							Rc2CannonicalCoord(_y, _height),
							_zPos
						);
		float3 L = m_Vp->m_PointLight.PointLightEquation(pos);
		// Count Lo - color of reflected light (Phong 5.4 second equation)
		float3 Lo = m_Vp->m_PointLight.Phong(L, pos, _normal, 100);
		// Add color += Lo (check max 0xFF per channel)
		r += (int) (Lo.r * 255.0f);

		if (r > 255)
			r = 255;

		g += (int) (Lo.g * 255.0f);
						
		if (g > 255)
			g = 255;

		b += (int) (Lo.b * 255.0f);
						
		if (b > 255)
			b = 255;

		retColor <<= 8;
		retColor |= r;
		retColor <<= 8;
		retColor |= g;
		retColor <<= 8;
		retColor |= b;
						
		return retColor;
	}
}